using System;
using System.Collections.Generic;
using GameFramework.Fsm;
using LS.Scene;
using LS.UI;
using UnityEngine;
using UnityGameFramework.Runtime;

namespace LS.Procedure
{
    /// <summary>
    /// 游戏流程基类
    /// </summary>
    public abstract partial class GameProcedureBase : ProcedureBase
    {
        public override bool UseNativeDialog => false;

        private Type m_NextProcedureType = null;
        private bool m_ChangeNextProcedureTrigger = false;
        
        protected readonly HashSet<SceneReference> m_SceneReferences = new HashSet<SceneReference>();

        protected override void OnLeave(IFsm<GameFramework.Procedure.IProcedureManager> procedureOwner, bool isShutdown)
        {
            m_NextProcedureType = null;
            CallSceneReferenceLeave();
            base.OnLeave(procedureOwner, isShutdown);
        }

        protected override void OnUpdate(IFsm<GameFramework.Procedure.IProcedureManager> procedureOwner, float elapseSeconds, float realElapseSeconds)
        {
            base.OnUpdate(procedureOwner, elapseSeconds, realElapseSeconds);
            if (m_ChangeNextProcedureTrigger)
            {
                Type procedureType = m_NextProcedureType;
                m_NextProcedureType = null;
                m_ChangeNextProcedureTrigger = false;
                ChangeState(procedureOwner, procedureType);
            }
        }

        protected virtual void CallSceneReferenceEnter()
        {
            CallSceneReferenceEnter<SceneReference>();
        }

        protected virtual void CallSceneReferenceLeave()
        {
            CallSceneReferenceLeave<SceneReference>();
        }
        
        protected void CallSceneReferenceEnter<T>() where T : SceneReference
        {
            var references = GameObject.FindObjectsByType<T>(FindObjectsSortMode.None);
            for (int i = 0; i < references.Length; i++)
            {
                var reference = references[i];
                if (m_SceneReferences.Contains(reference))
                {
                    continue;
                }
                m_SceneReferences.Add(reference);
                reference.OnEntry(this);
            }
        }
        
        protected void CallSceneReferenceLeave<T>() where T : SceneReference
        {
            var references = GameObject.FindObjectsByType<T>(FindObjectsSortMode.None);
            for (int i = 0; i < references.Length; i++)
            {
                var reference = references[i];
                if (!m_SceneReferences.Contains(reference))
                {
                    continue;
                }
                m_SceneReferences.Remove(reference);
                reference.OnLeave(this);
            }
        }
        
        public virtual void ChangeNextProcedure(Type procedureType)
        {
            m_ChangeNextProcedureTrigger = true;
            m_NextProcedureType = procedureType;
        }

        public virtual void ChangeNextProcedure<T>() where T: GameFramework.Procedure.ProcedureBase
        {
            ChangeNextProcedure(typeof(T));
        }

        public virtual void OpenLoadingForm()
        {
            int? serialId = LSEntry.UI.OpenUIForm(UIFormId.LoadingForm);
            if (serialId.HasValue)
            {
                var node = LSEntry.DataNode.GetNode("UIForm.LoaderForm.SerialId");
                if (node == null)
                {
                    LSEntry.DataNode.SetData<VarInt32>("UIForm.LoaderForm.SerialId",serialId.Value);
                }
                else
                {
                    var data = node.GetData<VarInt32>();
                    if (data == null)
                        node.SetData((VarInt32)serialId.Value);
                    else
                        data.Value = serialId.Value;
                }
            }
        }
        
        public virtual void CloseLoadingForm()
        {
            var node = LSEntry.DataNode.GetNode("UIForm.LoaderForm.SerialId");
            if (node != null)
            {
                int? serialId = node.GetData<VarInt32>()?.Value;
                if (serialId.HasValue)
                {
                    LSEntry.DataNode.RemoveNode("UIForm.LoaderForm.SerialId");
                    LSEntry.UI.CloseUIForm(serialId.Value);
                }
            }
        }

        public virtual void SceneReferencePrepareStart(SceneReference sceneReference)
        {
            
        }
        
        public virtual void SceneReferencePrepareFinish(SceneReference sceneReference)
        {
            CloseLoadingForm();
        }
    }
}
